﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.Contracts;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SDTViewer
{
    public abstract class SDT: IDisposable
    {
        public const byte DTYPE_I = (byte)'i';
        public const byte DTYPE_F = (byte)'f';
        protected byte dtype = DTYPE_I;
        protected string[] headers = null;
        public abstract DataTable DataTable { get; }
        public string[] Head { get => headers; }
        public byte DType { get => dtype; }
       
        public abstract void Read(string filename);
        public static byte GetSDTFileType(string filename)
        {
            Stream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
            if (filename.ToLower().EndsWith(".gz"))
            {
                fs = new GZipStream(fs, CompressionMode.Decompress);
            }
            var br = new BinaryReader(fs);
            br.ReadInt32();
            var dtype = br.ReadByte();
            if (dtype != DTYPE_I && dtype != DTYPE_F) { throw new InvalidDataException("数据类型无效"); }
            return dtype;
        }
        public static SDT LoadSDTFile(string filename)
        {
            if (GetSDTFileType(filename) == DTYPE_I)
            {
                return new IntSDT(filename);
            }
            else
            {
                return new FloatSDT(filename);
            }
        }
        protected BinaryReader readHeader(string filename)
        {
            Stream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
            if (filename.ToLower().EndsWith(".gz"))
            {
                fs = new GZipStream(fs, CompressionMode.Decompress);
            }
            var br = new BinaryReader(fs,Encoding.ASCII);
            var headLen = br.ReadInt32();
            dtype = br.ReadByte();
            if (dtype != DTYPE_I && dtype != DTYPE_F) { throw new InvalidDataException("数据类型无效"); }
            headers = Encoding.UTF8.GetString(br.ReadBytes(headLen - 1)).TrimEnd().Split('|');
            return br;
        }
        public abstract void Save(string filename);
        protected BinaryWriter saveHeader(string filename)
        {
            Stream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
            if (filename.ToLower().EndsWith(".gz"))
            {
                fs = new GZipStream(fs, CompressionMode.Compress);
            }
            var bw = new BinaryWriter(fs,Encoding.ASCII);
            byte[] s = Encoding.ASCII.GetBytes(string.Join("|", headers));
            int headLen = s.Length + 1;
            var rem = (headLen - 1) % 4 + 1;
            headLen = headLen - rem + 4;
            bw.Write(headLen);
            bw.Write(dtype);
            bw.Write(s);
            bw.Write(Encoding.ASCII.GetBytes(new string(' ', headLen - s.Length - 1)));
            return bw;
        }

        public void Dispose()
        {
            DataTable.Dispose();
        }
    }
    public abstract class SDT<T> : SDT, IDisposable
    {
        DataTable data;
        public override DataTable DataTable { get => data; }
        private void newDT()
        {
            data = new DataTable();
            foreach (var h in headers)
            {
                data.Columns.Add(h, typeof(T));
            }
        }
        public SDT(string[] _heads, byte _dtype)
        {
            headers = _heads;
            dtype = _dtype;
            newDT();
        }
        public SDT(string filename)
        {
            Read(filename);
        }
        protected abstract T FromBytes(byte[] bytes);
        protected abstract byte[] ToBytes(T val);

        public override void Read(string filename)
        {
            var br = readHeader(filename);
            var col_cnt = headers.Length;
            newDT();
            do
            {
                var newrow = data.NewRow();
                var bytes = br.ReadBytes(4);
                if (bytes.Length < 4) break;
                newrow[0] = FromBytes(bytes);
                for (int i = 1; i < col_cnt; ++i)
                {
                    newrow[i] = FromBytes(br.ReadBytes(4));
                }
                data.Rows.Add(newrow);
            } while (true);
            br.Close();
        }

        public override void Save(string filename)
        {
            var bw = saveHeader(filename);
            var cols = headers.Length;
            var rows = data.Rows.Count;
            for(int i=0; i<rows; ++i)
            {
                for(int j=0; j<cols; ++j)
                {
                    bw.Write(ToBytes((T)data.Rows[i][j]));
                }
            }
            bw.Close();
        }

    }
    public class IntSDT : SDT<int>
    {
        public IntSDT(string[] header, byte dtype) : base(header, dtype)
        {
        }

        public IntSDT(string filename) : base(filename)
        {
        }

        protected override int FromBytes(byte[] bytes)
        {
            return BitConverter.ToInt32(bytes, 0);
        }

        protected override byte[] ToBytes(int val)
        {
            return BitConverter.GetBytes(val);
        }
    }
    public class FloatSDT : SDT<float>
    {
        public FloatSDT(string[] header, byte dtype) : base(header, dtype)
        {
        }

        public FloatSDT(string filename) : base(filename)
        {
        }

        protected override float FromBytes(byte[] bytes)
        {
            return BitConverter.ToSingle(bytes, 0);
        }

        protected override byte[] ToBytes(float val)
        {
            return BitConverter.GetBytes(val);
        }
    }
}
